﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Diagnostics;
using System.Net;
using Svg;
using System.Xml;

namespace Helpers
{
    // State object for receiving data from remote device.
    public class StateObject
    {
        // Client socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 256;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder content = new StringBuilder();
        public List<byte> sb = new List<byte>();
        public string response = String.Empty;
        public int width = 400;
        public int height = 400;
    }
    public class SkecthHelper
    {
        // The port number for the remote device.
        private const int port = 11000;

        // ManualResetEvent instances signal completion.
        private static ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private static ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private static ManualResetEvent receiveDone =
            new ManualResetEvent(false);

        // The response from the remote device.

        private static StateObject StartClient(byte[] file, string imagePath)
        {
            StateObject state = null;
            connectDone.Reset();
            sendDone.Reset();
            receiveDone.Reset();
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
                // The name of the 
                // remote device is "host.contoso.com".
                IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
                IPAddress ipAddress = ipHostInfo.AddressList[0];
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP socket.
                Socket client = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);

                // Connect to the remote endpoint.
                client.BeginConnect(remoteEP,
                    new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();

                // Send test data to the remote device.
                byte[] data = file;
                Send(client, data, imagePath);
                sendDone.WaitOne();

                // Receive the response from the remote device.
                state = Receive(client);
                receiveDone.WaitOne();

                // Release the socket.
                client.Shutdown(SocketShutdown.Both);
                client.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return state;
        }
        public static string GetHumanFace(byte[] file, string imagePath)
        {
            StateObject state = StartClient(file, imagePath);
            if (state != null)
                return state.response;
            else return String.Empty;
        }
        public static Bitmap GetHumanFaceImage(byte[] file, string imagePath, string siteRootPath)
        {
            string svg = GetHumanFace(file, imagePath);
            StringReader sreader = new StringReader(svg);
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(sreader);
            SvgDocument svgDoc = SvgDocument.Open(xmlDoc);
            svgDoc.Height = 305;
            svgDoc.Width = 400;
            Bitmap bmp = svgDoc.Draw();
            return bmp;
        }

        private static StateObject Receive(Socket client)
        {
            StateObject state = new StateObject();
            try
            {
                // Create the state object.

                state.workSocket = client;

                // Begin receiving the data from the remote device.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return state;
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                String content = String.Empty;

                // Retrieve the state object and the handler socket
                // from the asynchronous state object.
                StateObject state = (StateObject)ar.AsyncState;
                Socket handler = state.workSocket;

                // Read data from the client socket. 
                int bytesRead = handler.EndReceive(ar);

                if (bytesRead > 0)
                {
                    state.content.Append(Encoding.ASCII.GetString(
                       state.buffer, 0, bytesRead));
                    content = state.sb.ToString();

                    string eof = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
                    // There  might be more data, so store the data received so far.
                    int indexEOF = eof.IndexOf("<EOF>");
                    if (eof.IndexOf("<EOF>") > -1)
                    {
                        for (int i = 0; i < indexEOF; i++)
                        {
                            state.sb.Add(state.buffer[i]);
                        }
                    }
                    else
                    {
                        for (int i = 0; i < bytesRead; i++)
                        {
                            state.sb.Add(state.buffer[i]);
                        }
                    }
                    if (eof.IndexOf("<EOF>") > -1)
                    {
                        // All the data has been read from the 
                        // client. Display it on the console.
                        byte[] byteData = state.sb.ToArray();
                        byte[] imagePathLen = new byte[sizeof(int)];
                        Array.Copy(byteData, imagePathLen, imagePathLen.Length);
                        byte[] imagePath = new byte[BitConverter.ToInt32(imagePathLen, 0)];
                        for (int i = 0; i < imagePath.Length; i++)
                        {
                            imagePath[i] = byteData[imagePathLen.Length + i];
                        }
                        string imgPath = Encoding.ASCII.GetString(imagePath, 0, imagePath.Length);
                        byte[] data = new byte[byteData.Length - (imagePath.Length + imagePathLen.Length)];
                        for (int i = 0; i < data.Length; i++)
                        {
                            data[i] = byteData[(imagePath.Length + imagePathLen.Length) + i];
                        }

                        string svg = Encoding.ASCII.GetString(data, 0, data.Length);
                        state.response = svg;
                        receiveDone.Set();
                    }
                    else
                    {
                        // Not all data received. Get more.
                        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);
                    }
                }
            }
            catch (InvalidOperationException ex)
            {
                Trace.WriteLine(ex.ToString(), "Information");
            }
        }

        private static void Send(Socket client, byte[] data, string imgPath)
        {
            MemoryStream mStream = new MemoryStream(data);
            Image b = Image.FromStream(mStream);
            int width = 0;
            int height = 0;
            if (b.PhysicalDimension.Height > b.PhysicalDimension.Width)
            {
                if (b.PhysicalDimension.Height > 400)
                {
                    width = (int)((400 / b.PhysicalDimension.Height) * b.PhysicalDimension.Width);
                    height = 400;
                }
                else
                {
                    width = (int)b.PhysicalDimension.Width;
                    height = (int)b.PhysicalDimension.Height;
                }
            }
            else
            {
                if (b.PhysicalDimension.Width > 400)
                {
                    height = (int)((400 / b.PhysicalDimension.Width) * b.PhysicalDimension.Height);
                    width = 400;
                }
                else
                {
                    width = (int)b.PhysicalDimension.Width;
                    height = (int)b.PhysicalDimension.Height;
                }
            }
            Bitmap result = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage((Image)result))
                g.DrawImage(b, 0, 0, width, height);

            MemoryStream stream = new MemoryStream();
            result.Save(stream, ImageFormat.Jpeg);

            data = stream.ToArray();

            result.Dispose();
            mStream.Close();
            stream.Close();

            imgPath = Path.GetFileName(imgPath);
            // Begin sending the data to the remote device.
            byte[] eof = Encoding.ASCII.GetBytes("<EOF>");
            byte[] imagePathLen = BitConverter.GetBytes(imgPath.Length);
            byte[] imagePath = Encoding.ASCII.GetBytes(imgPath);
            byte[] byteData = new byte[imagePathLen.Length + imagePath.Length + data.Length + eof.Length];
            imagePathLen.CopyTo(byteData, 0);
            imagePath.CopyTo(byteData, imagePathLen.Length);
            data.CopyTo(byteData, imagePathLen.Length + imagePath.Length);
            eof.CopyTo(byteData, imagePathLen.Length + imagePath.Length + data.Length);

            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), client);
        }
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = client.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to server.", bytesSent);

                // Signal that all bytes have been sent.
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete the connection.
                client.EndConnect(ar);

                Trace.WriteLine(String.Format("Socket connected to {0}", client.RemoteEndPoint.ToString()), "Information");

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception e)
            {
                Trace.WriteLine(e.ToString(), "Information");
            }
        }
    }
}
